[TOC]

# 1. CH2O

```python
# -*- coding: utf-8 -*-
"""
甲醛检测分析
作者常凯COVK、LDXY21DZXXGC2Class
"""

# ==================== 导入库 ====================
import pandas as pd
import numpy as np
from datetime import datetime
from docx import Document
from docx.shared import Pt, Inches
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.oxml.ns import qn
import os
import docx2pdf
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import threading

# 设置中文字体和解决负号显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# ==================== 默认配置 ====================
DEFAULT_CONFIG = {
    # 默认保存路径
    "default_dir": r"C:\Users\DELL\Desktop\learn\测试部分数据\测试报告",

    # 质量评价标准
    "thresholds": {
        '优（安全）': 0.08,  # Ⅰ级标准：≤0.08 mg/m³（1小时均值）
        '良（预警）': 0.10,  # Ⅱ级标准：0.08-0.10 mg/m³
        '差（超标）': 1.00  # 超标：>0.10 mg/m³
    },

    # 文档格式配置
    "font_name": "宋体",
    "title_font_size": 16,
    "table_style": "Light Shading Accent 1",

    # 列名配置
    "time_col": "时间",
    "value_col": "CH2O检测值"
}


class HCHOAnalyzerUI:
    def __init__(self, root):
        self.root = root
        self.root.title("甲醛浓度测试系统")
        self.root.geometry("550x400")

        # 存储路径变量
        self.excel_path = tk.StringVar()
        self.word_path = tk.StringVar()
        self.pdf_path = tk.StringVar()
        self.plot_path = tk.StringVar()

        # 设置默认路径
        self.set_default_paths()

        # 主框架
        main_frame = ttk.Frame(root, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        ttk.Label(main_frame, text="甲醛浓度测试系统", font=('Arial', 14)).pack(pady=10)

        # 文件选择区域
        self.create_file_selection_ui(main_frame)

        # 分析按钮
        self.analyze_btn = ttk.Button(
            main_frame,
            text="开始分析",
            command=self.start_analysis,
            width=20
        )
        self.analyze_btn.pack(pady=20)

        # 状态标签
        self.status_label = ttk.Label(main_frame, text="准备就绪", foreground="gray")
        self.status_label.pack()

        # 进度条
        self.progress = ttk.Progressbar(main_frame, orient=tk.HORIZONTAL, length=300, mode='determinate')

    def set_default_paths(self):
        """设置默认路径"""
        base_dir = DEFAULT_CONFIG["default_dir"]
        self.word_path.set(os.path.join(base_dir, "甲醛CH2O报告.docx"))
        self.pdf_path.set(os.path.join(base_dir, "甲醛CH2O报告.pdf"))
        self.plot_path.set(os.path.join(base_dir, "甲醛浓度折线图.png"))

    def create_file_selection_ui(self, parent):
        """创建文件选择界面"""
        # Excel文件选择
        excel_frame = ttk.Frame(parent)
        excel_frame.pack(fill=tk.X, pady=5)
        ttk.Label(excel_frame, text="Excel文件:").pack(side=tk.LEFT)
        ttk.Entry(excel_frame, textvariable=self.excel_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(excel_frame, text="浏览", command=self.select_excel_file).pack(side=tk.LEFT)

        # Word报告选择
        word_frame = ttk.Frame(parent)
        word_frame.pack(fill=tk.X, pady=5)
        ttk.Label(word_frame, text="Word报告:").pack(side=tk.LEFT)
        ttk.Entry(word_frame, textvariable=self.word_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(word_frame, text="浏览", command=lambda: self.select_save_path("word")).pack(side=tk.LEFT)

        # PDF报告选择
        pdf_frame = ttk.Frame(parent)
        pdf_frame.pack(fill=tk.X, pady=5)
        ttk.Label(pdf_frame, text="PDF报告:").pack(side=tk.LEFT)
        ttk.Entry(pdf_frame, textvariable=self.pdf_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(pdf_frame, text="浏览", command=lambda: self.select_save_path("pdf")).pack(side=tk.LEFT)

        # 折线图选择
        plot_frame = ttk.Frame(parent)
        plot_frame.pack(fill=tk.X, pady=5)
        ttk.Label(plot_frame, text="折线图:").pack(side=tk.LEFT)
        ttk.Entry(plot_frame, textvariable=self.plot_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(plot_frame, text="浏览", command=lambda: self.select_save_path("plot")).pack(side=tk.LEFT)

    def select_excel_file(self):
        """选择Excel文件"""
        path = filedialog.askopenfilename(
            title="选择Excel文件",
            filetypes=[("Excel文件", "*.xlsx"), ("所有文件", "*.*")]
        )
        if path:
            self.excel_path.set(path)

    def select_save_path(self, file_type):
        """选择保存路径"""
        initial_dir = DEFAULT_CONFIG["default_dir"]
        filetypes = [("Word文档", "*.docx")]
        title = "保存Word报告"
        default_ext = ".docx"

        if file_type == "pdf":
            filetypes = [("PDF文档", "*.pdf")]
            title = "保存PDF报告"
            default_ext = ".pdf"
        elif file_type == "plot":
            filetypes = [("PNG图片", "*.png")]
            title = "保存折线图"
            default_ext = ".png"

        path = filedialog.asksaveasfilename(
            title=title,
            initialdir=initial_dir,
            filetypes=filetypes,
            defaultextension=default_ext
        )

        if path:
            if file_type == "word":
                self.word_path.set(path)
            elif file_type == "pdf":
                self.pdf_path.set(path)
            elif file_type == "plot":
                self.plot_path.set(path)

    def start_analysis(self):
        """开始分析"""
        # 验证Excel文件是否已选择
        if not self.excel_path.get():
            messagebox.showerror("错误", "请先选择Excel文件")
            return

        self.analyze_btn.config(state=tk.DISABLED)
        self.status_label.config(text="正在分析...", foreground="blue")
        self.progress.pack(pady=10)
        self.progress["value"] = 0
        self.root.update()

        # 在新线程中运行分析
        analysis_thread = threading.Thread(target=self.run_analysis, daemon=True)
        analysis_thread.start()

        # 启动进度条更新
        self.update_progress()

    def update_progress(self):
        """更新进度条"""
        if self.progress["value"] < 100:
            self.progress["value"] += 5
            self.root.after(200, self.update_progress)

    def run_analysis(self):
        """执行分析任务"""
        try:
            config = {
                "excel_path": self.excel_path.get(),
                "word_path": self.word_path.get(),
                "pdf_path": self.pdf_path.get(),
                "plot_path": self.plot_path.get(),** DEFAULT_CONFIG
            }

            # === 数据读取 ===
            self.update_status("正在读取Excel文件...")
            df = pd.read_excel(
                config['excel_path'],
                parse_dates=[config['time_col']],
                engine='openpyxl'
            )

            # === 数据验证 ===
            self.update_status("进行数据验证...")
            validate_data(df, config)

            # === 数据处理 ===
            self.update_status("执行质量评价...")
            evaluations = evaluate_hcho_with_ml(df, config)

            # === 生成折线图 ===
            self.update_status("生成甲醛浓度折线图...")
            plt.figure(figsize=(10, 8))
            time_data = np.array(df[config['time_col']])
            value_data = np.array(df[config['value_col']])
            plt.plot(time_data, value_data, marker='o')
            plt.xlabel('检测时间')
            plt.ylabel('甲醛浓度(mg/m³)')
            plt.title('甲醛浓度变化趋势')
            plt.xticks(rotation=45)
            plt.grid(True)
            plt.savefig(config['plot_path'])
            plt.close()

            # === 生成报告 ===
            self.update_status("生成Word报告...")
            create_word_report(df, evaluations, config)

            # === 将Word文档转换为PDF ===
            self.update_status("将Word报告转换为PDF...")
            docx2pdf.convert(config['word_path'], config['pdf_path'])

            # 分析完成
            self.progress["value"] = 100
            self.update_status("分析完成!", success=True)

            # 计算统计结果
            grade_counts = {
                '优（安全）': evaluations.count('优（安全）'),
                '良（预警）': evaluations.count('良（预警）'),
                '差（超标）': evaluations.count('差（超标）')
            }
            total = len(df)

            # 显示结果
            result_message = (
                f"分析完成！\n\n"
                f"Word报告: {config['word_path']}\n"
                f"PDF报告: {config['pdf_path']}\n\n"
                f"甲醛浓度分布（GB/T 18883-2022标准）:\n"
                f"- 安全范围：{grade_counts['优（安全）']}次（≤0.08mg/m³）\n"
                f"- 预警范围：{grade_counts['良（预警）']}次（0.08-0.10mg/m³）\n"
                f"- 超标警报：{grade_counts['差（超标）']}次（＞0.10mg/m³）"
            )
            messagebox.showinfo("分析完成", result_message)

        except FileNotFoundError:
            self.show_error(f"Excel文件不存在 {self.excel_path.get()}")
        except ValueError as ve:
            self.show_error(f"数据验证错误：{str(ve)}")
        except Exception as e:
            self.show_error(f"未预期的错误：{str(e)}")
        finally:
            # 恢复按钮状态
            self.analyze_btn.config(state=tk.NORMAL)

    def update_status(self, message, success=False):
        """更新状态标签"""
        self.status_label.config(text=message)
        if success:
            self.status_label.config(foreground="green")
        self.root.update()

    def show_error(self, message):
        """显示错误信息"""
        self.status_label.config(text="分析失败", foreground="red")
        self.progress["value"] = 0
        messagebox.showerror("错误", message)


# ==================== 数据验证函数 ====================
def validate_data(df, config):
    """验证甲醛数据有效性"""
    required_cols = [config['time_col'], config['value_col']]
    missing_cols = [col for col in required_cols if col not in df.columns]
    if missing_cols:
        raise ValueError(f"缺少必要列：{', '.join(missing_cols)}")

    # 验证数值范围（0-1.0 mg/m³，参考传感器量程）
    if (df[config['value_col']] < 0).any() or (df[config['value_col']] > 1.0).any():
        raise ValueError("检测值超出有效范围(0-1.0mg/m³)")


# ==================== 使用机器学习进行质量评价函数 ====================
def evaluate_hcho_with_ml(df, config):
    """使用决策树分类器进行甲醛质量等级评价"""
    # 准备数据
    X = df[[config['value_col']]]  # 特征
    y = df[config['value_col']].apply(lambda x: '优（安全）' if x <= config['thresholds']['优（安全）']
    else ('良（预警）' if x <= config['thresholds']['良（预警）']
          else '差（超标）'))  # 标签

    # 划分训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

    # 训练模型
    model = DecisionTreeClassifier()
    model.fit(X_train, y_train)

    # 预测
    y_pred = model.predict(X)

    # 评估模型
    accuracy = accuracy_score(y, y_pred)
    print(f"模型准确率: {accuracy}")

    return y_pred.tolist()


# ==================== Word报告生成函数 ====================
def create_word_report(df, evaluations, config):
    """生成甲醛检测报告（含健康建议）"""
    # 确保保存目录存在
    os.makedirs(os.path.dirname(config['word_path']), exist_ok=True)

    doc = Document()

    # ---- 全局字体设置 ----
    doc.styles['Normal'].font.name = config['font_name']
    doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), config['font_name'])

    # ---- 报告标题 ----
    title = doc.add_paragraph()
    title.add_run("室内甲醛浓度检测报告").font.size = Pt(config['title_font_size'])
    title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER

    # ---- 基本信息段 ----
    doc.add_paragraph(f"检测标准：GB/T 18883-2022（1小时均值≤0.08mg/m³）")
    doc.add_paragraph(f"数据周期：{df[config['time_col']].min()} 至 {df[config['time_col']].max()}")

    # ---- 添加折线图 ----
    doc.add_heading('甲醛浓度变化趋势', level=1)
    doc.add_picture(config['plot_path'], width=Inches(6))

    doc.add_page_break()  # 分页
    # ---- 数据明细表 ----
    doc.add_heading('检测数据明细', level=1)
    table = doc.add_table(rows=1, cols=3, style=config['table_style'])
    header = table.rows[0].cells
    header[0].text = '记录时间'
    header[1].text = '甲醛浓度(mg/m³)'
    header[2].text = '安全等级'

    for _, row in df.iterrows():
        cells = table.add_row().cells
        cells[0].text = str(row[config['time_col']])
        cells[1].text = f"{row[config['value_col']]:.3f}"  # 保留3位小数
        cells[2].text = evaluations[_]

    # ---- 统计分析段 ----
    grade_counts = {
        '优（安全）': evaluations.count('优（安全）'),
        '良（预警）': evaluations.count('良（预警）'),
        '差（超标）': evaluations.count('差（超标）')
    }
    total = len(df)

    doc.add_heading('空气质量评估', level=1)
    stats = [
        ("达标时段", f"{grade_counts['优（安全）']}次 ({grade_counts['优（安全）'] / total:.1%})"),
        ("预警时段", f"{grade_counts['良（预警）']}次 ({grade_counts['良（预警）'] / total:.1%})"),
        ("超标时段", f"{grade_counts['差（超标）']}次 ({grade_counts['差（超标）'] / total:.1%})"),
        ("峰值浓度", f"{df[config['value_col']].max():.3f} mg/m³"),
        ("平均浓度", f"{df[config['value_col']].mean():.3f} mg/m³")
    ]

    for label, value in stats:
        p = doc.add_paragraph(style='ListBullet')
        p.add_run(label).bold = True
        p.add_run(f": {value}")

    # ---- 健康建议----
    doc.add_heading('治理建议', level=2)
    suggestions = [
        "超标处理：立即通风，使用活性炭/光触媒治理",
        "长期暴露：浓度＞0.1mg/m³可能引发白血病等疾病"
    ]
    for item in suggestions:
        doc.add_paragraph(item, style='ListNumber')

    doc.save(config['word_path'])


# ==================== 主程序 ====================
if __name__ == "__main__":
    root = tk.Tk()
    app = HCHOAnalyzerUI(root)
    root.mainloop()
```

# 2.CO2

```python
# -*- coding: utf-8 -*-
"""
CO2浓度检测分析
作者常凯COVK、LDXY21DZXXGC2Class
"""

# ==================== 导入库 ====================
import pandas as pd
from datetime import datetime
from docx import Document
from docx.shared import Pt, Inches
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.oxml.ns import qn
import os
import docx2pdf
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import threading

# 设置中文字体和解决负号显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# ==================== 默认配置 ====================
DEFAULT_CONFIG = {
    # 默认保存路径
    "default_dir": r"C:\Users\DELL\Desktop\learn\测试部分数据\测试报告",

    # 分级标准（参考GB/T 18883-2022和我自定义）
    "thresholds": {
        '优': 500,  # <500ppm
        '良': 1000,  # 500-1000ppm
        '差（需净化）': 9999  # >1000ppm
    },

    # 文档格式
    "font_name": "宋体",
    "title_font_size": 16,
    "table_style": "Light Shading Accent 1",

    # 列名配置
    "time_col": "时间",
    "value_col": "CO2检测值"
}


class CO2AnalyzerUI:
    def __init__(self, root):
        self.root = root
        self.root.title("CO2浓度测试系统")
        self.root.geometry("550x400")

        # 存储路径变量
        self.excel_path = tk.StringVar()
        self.word_path = tk.StringVar()
        self.pdf_path = tk.StringVar()
        self.plot_path = tk.StringVar()

        # 设置默认路径
        self.set_default_paths()

        # 主框架
        main_frame = ttk.Frame(root, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        ttk.Label(main_frame, text="CO2浓度测试系统", font=('Arial', 14)).pack(pady=10)

        # 文件选择区域
        self.create_file_selection_ui(main_frame)

        # 分析按钮
        self.analyze_btn = ttk.Button(
            main_frame,
            text="开始分析",
            command=self.start_analysis,
            width=20
        )
        self.analyze_btn.pack(pady=20)

        # 状态标签
        self.status_label = ttk.Label(main_frame, text="准备就绪", foreground="gray")
        self.status_label.pack()

        # 进度条
        self.progress = ttk.Progressbar(main_frame, orient=tk.HORIZONTAL, length=300, mode='determinate')

    def set_default_paths(self):
        """设置默认路径"""
        base_dir = DEFAULT_CONFIG["default_dir"]
        self.word_path.set(os.path.join(base_dir, "CO2报告.docx"))
        self.pdf_path.set(os.path.join(base_dir, "CO2报告.pdf"))
        self.plot_path.set(os.path.join(base_dir, "CO2浓度折线图.png"))

    def create_file_selection_ui(self, parent):
        """创建文件选择界面"""
        # Excel文件选择
        excel_frame = ttk.Frame(parent)
        excel_frame.pack(fill=tk.X, pady=5)
        ttk.Label(excel_frame, text="Excel文件:").pack(side=tk.LEFT)
        ttk.Entry(excel_frame, textvariable=self.excel_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(excel_frame, text="浏览", command=self.select_excel_file).pack(side=tk.LEFT)

        # Word报告选择
        word_frame = ttk.Frame(parent)
        word_frame.pack(fill=tk.X, pady=5)
        ttk.Label(word_frame, text="Word报告:").pack(side=tk.LEFT)
        ttk.Entry(word_frame, textvariable=self.word_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(word_frame, text="浏览", command=lambda: self.select_save_path("word")).pack(side=tk.LEFT)

        # PDF报告选择
        pdf_frame = ttk.Frame(parent)
        pdf_frame.pack(fill=tk.X, pady=5)
        ttk.Label(pdf_frame, text="PDF报告:").pack(side=tk.LEFT)
        ttk.Entry(pdf_frame, textvariable=self.pdf_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(pdf_frame, text="浏览", command=lambda: self.select_save_path("pdf")).pack(side=tk.LEFT)

        # 折线图选择
        plot_frame = ttk.Frame(parent)
        plot_frame.pack(fill=tk.X, pady=5)
        ttk.Label(plot_frame, text="折线图:").pack(side=tk.LEFT)
        ttk.Entry(plot_frame, textvariable=self.plot_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(plot_frame, text="浏览", command=lambda: self.select_save_path("plot")).pack(side=tk.LEFT)

    def select_excel_file(self):
        """选择Excel文件"""
        path = filedialog.askopenfilename(
            title="选择Excel文件",
            filetypes=[("Excel文件", "*.xlsx"), ("所有文件", "*.*")]
        )
        if path:
            self.excel_path.set(path)

    def select_save_path(self, file_type):
        """选择保存路径"""
        initial_dir = DEFAULT_CONFIG["default_dir"]
        filetypes = [("Word文档", "*.docx")]
        title = "保存Word报告"
        default_ext = ".docx"

        if file_type == "pdf":
            filetypes = [("PDF文档", "*.pdf")]
            title = "保存PDF报告"
            default_ext = ".pdf"
        elif file_type == "plot":
            filetypes = [("PNG图片", "*.png")]
            title = "保存折线图"
            default_ext = ".png"

        path = filedialog.asksaveasfilename(
            title=title,
            initialdir=initial_dir,
            filetypes=filetypes,
            defaultextension=default_ext
        )

        if path:
            if file_type == "word":
                self.word_path.set(path)
            elif file_type == "pdf":
                self.pdf_path.set(path)
            elif file_type == "plot":
                self.plot_path.set(path)

    def start_analysis(self):
        """开始分析"""
        # 验证Excel文件是否已选择
        if not self.excel_path.get():
            messagebox.showerror("错误", "请先选择Excel文件")
            return

        self.analyze_btn.config(state=tk.DISABLED)
        self.status_label.config(text="正在分析...", foreground="blue")
        self.progress.pack(pady=10)
        self.progress["value"] = 0
        self.root.update()

        # 在新线程中运行分析
        analysis_thread = threading.Thread(target=self.run_analysis, daemon=True)
        analysis_thread.start()

        # 启动进度条更新
        self.update_progress()

    def update_progress(self):
        """更新进度条"""
        if self.progress["value"] < 100:
            self.progress["value"] += 5
            self.root.after(200, self.update_progress)

    def run_analysis(self):
        """执行分析任务"""
        try:
            config = {
                "excel_path": self.excel_path.get(),
                "word_path": self.word_path.get(),
                "pdf_path": self.pdf_path.get(),
                "plot_path": self.plot_path.get(),** DEFAULT_CONFIG
            }

            # === 数据读取 ===
            self.update_status("正在读取Excel文件...")
            df = pd.read_excel(
                config['excel_path'],
                parse_dates=[config['time_col']],
                engine='openpyxl'
            )

            # === 数据验证 ===
            self.update_status("进行数据验证...")
            validate_data(df, config)

            # === 数据处理 ===
            self.update_status("执行质量评价...")
            evaluations = evaluate_co2_with_ml(df, config)

            # === 生成折线图 ===
            self.update_status("生成CO2浓度折线图...")
            plt.figure(figsize=(10, 8))
            plt.plot(
                df[config['time_col']].to_numpy(),
                df[config['value_col']].to_numpy(),
                marker='o'
            )
            plt.xlabel('检测时间')
            plt.ylabel('CO2浓度(ppm)')
            plt.title('CO2浓度变化趋势')
            plt.xticks(rotation=45)
            plt.grid(True)
            plt.savefig(config['plot_path'])
            plt.close()

            # === 生成报告 ===
            self.update_status("生成Word报告...")
            create_word_report(df, evaluations, config)

            # === 将Word文档转换为PDF ===
            self.update_status("将Word报告转换为PDF...")
            docx2pdf.convert(config['word_path'], config['pdf_path'])

            # 分析完成
            self.progress["value"] = 100
            self.update_status("分析完成!", success=True)

            # 显示结果
            result_message = (
                f"分析完成！\n\n"
                f"Word报告: {config['word_path']}\n"
                f"PDF报告: {config['pdf_path']}\n\n"
                f"共分析{len(df)}条数据，其中:\n"
                f"- 优质: {evaluations.count('优')}次\n"
                f"- 良好: {evaluations.count('良')}次\n"
                f"- 需净化: {evaluations.count('差（需净化）')}次"
            )
            messagebox.showinfo("分析完成", result_message)

        except FileNotFoundError:
            self.show_error(f"Excel文件不存在 {self.excel_path.get()}")
        except ValueError as ve:
            self.show_error(f"数据验证错误：{str(ve)}")
        except Exception as e:
            self.show_error(f"未预期的错误：{str(e)}")
        finally:
            # 恢复按钮状态
            self.analyze_btn.config(state=tk.NORMAL)

    def update_status(self, message, success=False):
        """更新状态标签"""
        self.status_label.config(text=message)
        if success:
            self.status_label.config(foreground="green")
        self.root.update()

    def show_error(self, message):
        """显示错误信息"""
        self.status_label.config(text="分析失败", foreground="red")
        self.progress["value"] = 0
        messagebox.showerror("错误", message)


# ==================== 数据验证 ====================
def validate_data(df, config):
    """验证数据有效性"""
    required_cols = [config['time_col'], config['value_col']]
    missing_cols = [col for col in required_cols if col not in df.columns]
    if missing_cols:
        raise ValueError(f"缺少必要列：{', '.join(missing_cols)}")

    # 数值范围验证（传感器有效范围400-5000ppm）
    if (df[config['value_col']] < 100).any() or (df[config['value_col']] > 5000).any():
        raise ValueError("检测值超出有效范围(400-5000ppm)")


# ==================== 使用机器学习进行质量评价 ====================
def evaluate_co2_with_ml(df, config):
    """使用决策树分类器进行CO2分级评价"""
    # 准备数据
    X = df[[config['value_col']]]  # 特征
    y = df[config['value_col']].apply(lambda x: '优' if x < 500 else ('良' if 500 <= x <= 1000 else '差（需净化）'))  # 标签

    # 划分训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

    # 训练模型
    model = DecisionTreeClassifier()
    model.fit(X_train, y_train)

    # 预测
    y_pred = model.predict(X)

    # 评估模型
    accuracy = accuracy_score(y, y_pred)
    print(f"模型准确率: {accuracy}")

    return y_pred.tolist()


# ==================== 报告生成 ====================
def create_word_report(df, evaluations, config):
    """生成分析报告"""
    # 确保保存目录存在
    os.makedirs(os.path.dirname(config['word_path']), exist_ok=True)

    doc = Document()

    # 设置字体
    doc.styles['Normal'].font.name = config['font_name']
    doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), config['font_name'])

    # 标题
    title = doc.add_paragraph()
    title.add_run("CO2浓度检测报告").font.size = Pt(config['title_font_size'])
    title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER

    # 基础信息
    doc.add_paragraph(f"生成时间：{datetime.now().strftime('%Y-%m-%d %H:%M')}")
    doc.add_paragraph(f"数据记录数：{len(df)}条")

    # 添加折线图
    doc.add_heading('CO2浓度变化趋势', level=1)
    doc.add_picture(config['plot_path'], width=Inches(6))

    doc.add_page_break()  # 分页
    # 数据表格（设置数字格式）
    table = doc.add_table(rows=1, cols=3, style=config['table_style'])
    header = table.rows[0].cells
    header[0].text = '时间'
    header[1].text = 'CO2浓度(ppm)'
    header[2].text = '空气质量'

    for _, row in df.iterrows():
        cells = table.add_row().cells
        cells[0].text = str(row[config['time_col']])
        cells[1].text = f"{row[config['value_col']]:.0f}"  # 整数显示
        cells[2].text = evaluations[_]

    # 统计分析
    grade_counts = {
        '优': evaluations.count('优'),
        '良': evaluations.count('良'),
        '差（需净化）': evaluations.count('差（需净化）')
    }
    total = len(df)

    doc.add_heading('统计分析', level=1)
    stats = [
        ("优质时段", f"{grade_counts['优']}次 ({grade_counts['优'] / total:.1%})"),
        ("合格时段", f"{grade_counts['良']}次 ({grade_counts['良'] / total:.1%})"),
        ("需净化时段", f"{grade_counts['差（需净化）']}次 ({grade_counts['差（需净化）'] / total:.1%})"),
        ("最高浓度", f"{df[config['value_col']].max()} ppm"),
        ("平均浓度", f"{df[config['value_col']].mean():.0f} ppm")
    ]

    for label, value in stats:
        p = doc.add_paragraph(style='ListBullet')
        p.add_run(label).bold = True
        p.add_run(f": {value}")

    doc.save(config['word_path'])


# ==================== 主程序 ====================
if __name__ == "__main__":
    root = tk.Tk()
    app = CO2AnalyzerUI(root)
    root.mainloop()
```

# 3. PM2.5

```python
# -*- coding: utf-8 -*-
"""
PM2.5数据分析与报告生成
作者常凯COVK、LDXY21DZXXGC2Class
"""

# ==================== 导入库 ====================
import pandas as pd
import numpy as np
from datetime import datetime
from docx import Document
from docx.shared import Pt, Inches
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.oxml.ns import qn
import os
import docx2pdf
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import threading

# 设置中文字体和解决负号显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# ==================== 默认配置 ====================
DEFAULT_CONFIG = {
    # 默认保存路径
    "default_dir": r"C:\Users\DELL\Desktop\learn\测试部分数据\测试报告",

    # 质量评价标准（根据GB 3095-2012）
    "thresholds": {
        '优': 35,
        '良': 75,
        '差': 999
    },

    # 文档格式配置
    "font_name": "宋体",
    "title_font_size": 16,
    "table_style": "Light Shading Accent 1",

    # 列名配置
    "time_col": "时间",
    "value_col": "PM2.5检测值"
}


class PM25AnalyzerUI:
    def __init__(self, root):
        self.root = root
        self.root.title("PM2.5数据测试系统")
        self.root.geometry("550x400")

        # 存储路径变量
        self.excel_path = tk.StringVar()
        self.word_path = tk.StringVar()
        self.pdf_path = tk.StringVar()
        self.plot_path = tk.StringVar()

        # 设置默认路径
        self.set_default_paths()

        # 主框架
        main_frame = ttk.Frame(root, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        ttk.Label(main_frame, text="PM2.5数据测试系统", font=('Arial', 14)).pack(pady=10)

        # 文件选择区域
        self.create_file_selection_ui(main_frame)

        # 分析按钮
        self.analyze_btn = ttk.Button(
            main_frame,
            text="开始分析",
            command=self.start_analysis,
            width=20
        )
        self.analyze_btn.pack(pady=20)

        # 状态标签
        self.status_label = ttk.Label(main_frame, text="准备就绪", foreground="gray")
        self.status_label.pack()

        # 进度条
        self.progress = ttk.Progressbar(main_frame, orient=tk.HORIZONTAL, length=300, mode='determinate')

    def set_default_paths(self):
        """设置默认路径"""
        base_dir = DEFAULT_CONFIG["default_dir"]
        self.word_path.set(os.path.join(base_dir, "PM2.5报告.docx"))
        self.pdf_path.set(os.path.join(base_dir, "PM2.5报告.pdf"))
        self.plot_path.set(os.path.join(base_dir, "PM2.5折线图.png"))

    def create_file_selection_ui(self, parent):
        """创建文件选择界面"""
        # Excel文件选择
        excel_frame = ttk.Frame(parent)
        excel_frame.pack(fill=tk.X, pady=5)
        ttk.Label(excel_frame, text="Excel文件:").pack(side=tk.LEFT)
        ttk.Entry(excel_frame, textvariable=self.excel_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(excel_frame, text="浏览", command=self.select_excel_file).pack(side=tk.LEFT)

        # Word报告选择
        word_frame = ttk.Frame(parent)
        word_frame.pack(fill=tk.X, pady=5)
        ttk.Label(word_frame, text="Word报告:").pack(side=tk.LEFT)
        ttk.Entry(word_frame, textvariable=self.word_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(word_frame, text="浏览", command=lambda: self.select_save_path("word")).pack(side=tk.LEFT)

        # PDF报告选择
        pdf_frame = ttk.Frame(parent)
        pdf_frame.pack(fill=tk.X, pady=5)
        ttk.Label(pdf_frame, text="PDF报告:").pack(side=tk.LEFT)
        ttk.Entry(pdf_frame, textvariable=self.pdf_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(pdf_frame, text="浏览", command=lambda: self.select_save_path("pdf")).pack(side=tk.LEFT)

        # 折线图选择
        plot_frame = ttk.Frame(parent)
        plot_frame.pack(fill=tk.X, pady=5)
        ttk.Label(plot_frame, text="折线图:").pack(side=tk.LEFT)
        ttk.Entry(plot_frame, textvariable=self.plot_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(plot_frame, text="浏览", command=lambda: self.select_save_path("plot")).pack(side=tk.LEFT)

    def select_excel_file(self):
        """选择Excel文件"""
        path = filedialog.askopenfilename(
            title="选择Excel文件",
            filetypes=[("Excel文件", "*.xlsx"), ("所有文件", "*.*")]
        )
        if path:
            self.excel_path.set(path)

    def select_save_path(self, file_type):
        """选择保存路径"""
        initial_dir = DEFAULT_CONFIG["default_dir"]
        filetypes = [("Word文档", "*.docx")]
        title = "保存Word报告"
        default_ext = ".docx"

        if file_type == "pdf":
            filetypes = [("PDF文档", "*.pdf")]
            title = "保存PDF报告"
            default_ext = ".pdf"
        elif file_type == "plot":
            filetypes = [("PNG图片", "*.png")]
            title = "保存折线图"
            default_ext = ".png"

        path = filedialog.asksaveasfilename(
            title=title,
            initialdir=initial_dir,
            filetypes=filetypes,
            defaultextension=default_ext
        )

        if path:
            if file_type == "word":
                self.word_path.set(path)
            elif file_type == "pdf":
                self.pdf_path.set(path)
            elif file_type == "plot":
                self.plot_path.set(path)

    def start_analysis(self):
        """开始分析"""
        # 验证Excel文件是否已选择
        if not self.excel_path.get():
            messagebox.showerror("错误", "请先选择Excel文件")
            return

        self.analyze_btn.config(state=tk.DISABLED)
        self.status_label.config(text="正在分析...", foreground="blue")
        self.progress.pack(pady=10)
        self.progress["value"] = 0
        self.root.update()

        # 在新线程中运行分析
        analysis_thread = threading.Thread(target=self.run_analysis, daemon=True)
        analysis_thread.start()

        # 启动进度条更新
        self.update_progress()

    def update_progress(self):
        """更新进度条"""
        if self.progress["value"] < 100:
            self.progress["value"] += 5
            self.root.after(200, self.update_progress)

    def run_analysis(self):
        """执行分析任务"""
        try:
            config = {
                "excel_path": self.excel_path.get(),
                "word_path": self.word_path.get(),
                "pdf_path": self.pdf_path.get(),
                "plot_path": self.plot_path.get(),** DEFAULT_CONFIG
            }

            # === 数据读取 ===
            self.update_status("正在读取Excel文件...")
            df = pd.read_excel(
                config['excel_path'],
                parse_dates=[config['time_col']],
                engine='openpyxl'
            )

            # === 数据验证 ===
            self.update_status("进行数据验证...")
            validate_data(df, config)

            # === 数据处理 ===
            self.update_status("执行质量评价...")
            df = df.sort_values(config['time_col'])
            evaluations = evaluate_pm25_with_ml(df, config)

            # === 生成折线图 ===
            self.update_status("生成PM2.5浓度折线图...")
            plt.figure(figsize=(10, 8))
            time_data = np.array(df[config['time_col']])
            value_data = np.array(df[config['value_col']])
            plt.plot(time_data, value_data, marker='o')
            plt.xlabel('检测时间')
            plt.ylabel('PM2.5浓度（μg/m³）')
            plt.title('PM2.5浓度变化趋势')
            plt.xticks(rotation=45)
            plt.grid(True)
            plt.savefig(config['plot_path'])
            plt.close()

            # === 生成报告 ===
            self.update_status("生成Word报告...")
            create_word_report(df, evaluations, config)

            # === 将Word文档转换为PDF ===
            self.update_status("将Word报告转换为PDF...")
            docx2pdf.convert(config['word_path'], config['pdf_path'])

            # 分析完成
            self.progress["value"] = 100
            self.update_status("分析完成!", success=True)

            # 显示结果
            grade_counts = {
                '优': evaluations.count('优'),
                '良': evaluations.count('良'),
                '差': evaluations.count('差')
            }
            total = len(df)

            result_message = (
                f"分析完成！\n\n"
                f"Word报告: {config['word_path']}\n"
                f"PDF报告: {config['pdf_path']}\n\n"
                f"共分析{len(df)}条数据，其中:\n"
                f"- 优质: {grade_counts['优']}次\n"
                f"- 良好: {grade_counts['良']}次\n"
                f"- 污染: {grade_counts['差']}次"
            )
            messagebox.showinfo("分析完成", result_message)

        except FileNotFoundError:
            self.show_error(f"Excel文件不存在 {self.excel_path.get()}")
        except ValueError as ve:
            self.show_error(f"数据验证错误：{str(ve)}")
        except Exception as e:
            self.show_error(f"未预期的错误：{str(e)}")
        finally:
            # 恢复按钮状态
            self.analyze_btn.config(state=tk.NORMAL)

    def update_status(self, message, success=False):
        """更新状态标签"""
        self.status_label.config(text=message)
        if success:
            self.status_label.config(foreground="green")
        self.root.update()

    def show_error(self, message):
        """显示错误信息"""
        self.status_label.config(text="分析失败", foreground="red")
        self.progress["value"] = 0
        messagebox.showerror("错误", message)


# ==================== 数据验证函数 ====================
def validate_data(df, config):
    """验证PM2.5数据有效性"""
    required_cols = [config['time_col'], config['value_col']]
    missing_cols = [col for col in required_cols if col not in df.columns]
    if missing_cols:
        raise ValueError(f"缺少必要列：{', '.join(missing_cols)}")

    # 验证数值范围（0-500 μg/m³）
    if (df[config['value_col']] < 0).any() or (df[config['value_col']] > 500).any():
        raise ValueError("检测值超出有效范围(0-500μg/m³)")


# ==================== 使用机器学习进行质量评价函数 ====================
def evaluate_pm25_with_ml(df, config):
    """使用决策树分类器进行PM2.5质量等级评价"""
    # 准备数据
    X = df[[config['value_col']]]  # 特征
    y = df[config['value_col']].apply(lambda x: '优' if x <= config['thresholds']['优']
    else ('良' if x <= config['thresholds']['良']
          else '差'))  # 标签

    # 划分训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

    # 训练模型
    model = DecisionTreeClassifier()
    model.fit(X_train, y_train)

    # 预测
    y_pred = model.predict(X)

    # 评估模型
    accuracy = accuracy_score(y, y_pred)
    print(f"模型准确率: {accuracy}")

    return y_pred.tolist()


# ==================== Word报告生成函数 ====================
def create_word_report(df, evaluations, config):
    """生成PM2.5检测报告"""
    # 确保保存目录存在
    os.makedirs(os.path.dirname(config['word_path']), exist_ok=True)

    doc = Document()

    # 设置全局字体
    doc.styles['Normal'].font.name = config['font_name']
    doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), config['font_name'])

    # 添加标题
    title_para = doc.add_paragraph()
    title_run = title_para.add_run("PM2.5检测分析报告")
    title_run.font.size = Pt(config['title_font_size'])
    title_para.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER

    # 基本信息
    doc.add_paragraph(f"报告生成时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    doc.add_paragraph(f"数据文件：{os.path.basename(config['excel_path'])}")

    # 添加折线图
    doc.add_heading('PM2.5浓度变化趋势', level=1)
    doc.add_picture(config['plot_path'], width=Inches(6))

    doc.add_page_break()
    # 详细数据表
    doc.add_heading('检测数据明细', level=1)
    table = doc.add_table(rows=1, cols=3, style=config['table_style'])

    # 设置表头
    header_cells = table.rows[0].cells
    header_cells[0].text = '检测时间'
    header_cells[1].text = 'PM2.5浓度（μg/m³）'
    header_cells[2].text = '空气质量'

    # 填充数据行
    for idx, row in df.iterrows():
        cells = table.add_row().cells
        cells[0].text = str(row[config['time_col']])
        cells[1].text = f"{row[config['value_col']]:.1f}"
        cells[2].text = evaluations[idx]

    # 统计分析
    doc.add_heading('数据分析结果', level=1)

    # 质量分布统计
    grade_counts = {
        '优': evaluations.count('优'),
        '良': evaluations.count('良'),
        '差': evaluations.count('差')
    }
    total = len(df)

    # 添加统计段落
    stats = [
        ("总检测次数", f"{total}次"),
        ("优质天数", f"{grade_counts['优']}次 ({grade_counts['优'] / total:.1%})"),
        ("良好天数", f"{grade_counts['良']}次 ({grade_counts['良'] / total:.1%})"),
        ("污染天数", f"{grade_counts['差']}次 ({grade_counts['差'] / total:.1%})")
    ]
    for label, value in stats:
        p = doc.add_paragraph(style='ListBullet')
        p.add_run(label).bold = True
        p.add_run(f": {value}")

    # 数值特征
    doc.add_heading('浓度特征指标', level=2)
    numericals = [
        ("最大值", df[config['value_col']].max()),
        ("最小值", df[config['value_col']].min()),
        ("平均值", df[config['value_col']].mean()),
        ("标准差", df[config['value_col']].std())
    ]

    # 添加数值指标
    for name, val in numericals:
        doc.add_paragraph(
            f"{name}: {val:.1f} μg/m³",
            style='ListNumber'
        )

    # 保存文档
    doc.save(config['word_path'])


# ==================== 主程序 ====================
if __name__ == "__main__":
    root = tk.Tk()
    app = PM25AnalyzerUI(root)
    root.mainloop()
```

# 4. TVOC

```python
# -*- coding: utf-8 -*-
"""
TVOC数据分析与报告生成
作者常凯COVK、LDXY21DZXXGC2Class
"""

# ==================== 导入库 ====================
import pandas as pd
import numpy as np
from datetime import datetime
from docx import Document
from docx.shared import Pt, Inches
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.oxml.ns import qn
import os
import docx2pdf
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import threading

# 设置中文字体和解决负号显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# ==================== 默认配置 ====================
DEFAULT_CONFIG = {
    # 默认保存路径
    "default_dir": r"C:\Users\DELL\Desktop\learn\测试部分数据\测试报告",

    # 质量评价标准（依据GB/T 18883-2022）
    "thresholds": {
        '优（安全）': 0.6,  # I级标准：≤0.6 mg/m³
        '良（注意）': 1.0,  # II级标准：0.6-1.0 mg/m³
        '差（需净化）': 5.0  # 超标：>1.0 mg/m³（传感器最大量程5mg/m³）
    },

    # 文档格式配置
    "font_name": "宋体",
    "title_font_size": 16,
    "table_style": "Light Shading Accent 1",

    # 列名配置
    "time_col": "时间",
    "value_col": "TVOC检测值"
}


class TVOCAnalyzerUI:
    def __init__(self, root):
        self.root = root
        self.root.title("TVOC浓度分析系统")
        self.root.geometry("550x400")

        # 存储路径变量
        self.excel_path = tk.StringVar()
        self.word_path = tk.StringVar()
        self.pdf_path = tk.StringVar()
        self.plot_path = tk.StringVar()

        # 设置默认路径
        self.set_default_paths()

        # 主框架
        main_frame = ttk.Frame(root, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        ttk.Label(main_frame, text="TVOC浓度分析系统", font=('Arial', 14)).pack(pady=10)

        # 文件选择区域
        self.create_file_selection_ui(main_frame)

        # 分析按钮
        self.analyze_btn = ttk.Button(
            main_frame,
            text="开始分析",
            command=self.start_analysis,
            width=20
        )
        self.analyze_btn.pack(pady=20)

        # 状态标签
        self.status_label = ttk.Label(main_frame, text="准备就绪", foreground="gray")
        self.status_label.pack()

        # 进度条
        self.progress = ttk.Progressbar(main_frame, orient=tk.HORIZONTAL, length=300, mode='determinate')

    def set_default_paths(self):
        """设置默认路径"""
        base_dir = DEFAULT_CONFIG["default_dir"]
        self.word_path.set(os.path.join(base_dir, "TVOC报告.docx"))
        self.pdf_path.set(os.path.join(base_dir, "TVOC报告.pdf"))
        self.plot_path.set(os.path.join(base_dir, "TVOC浓度折线图.png"))

    def create_file_selection_ui(self, parent):
        """创建文件选择界面"""
        # Excel文件选择
        excel_frame = ttk.Frame(parent)
        excel_frame.pack(fill=tk.X, pady=5)
        ttk.Label(excel_frame, text="Excel文件:").pack(side=tk.LEFT)
        ttk.Entry(excel_frame, textvariable=self.excel_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(excel_frame, text="浏览", command=self.select_excel_file).pack(side=tk.LEFT)

        # Word报告选择
        word_frame = ttk.Frame(parent)
        word_frame.pack(fill=tk.X, pady=5)
        ttk.Label(word_frame, text="Word报告:").pack(side=tk.LEFT)
        ttk.Entry(word_frame, textvariable=self.word_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(word_frame, text="浏览", command=lambda: self.select_save_path("word")).pack(side=tk.LEFT)

        # PDF报告选择
        pdf_frame = ttk.Frame(parent)
        pdf_frame.pack(fill=tk.X, pady=5)
        ttk.Label(pdf_frame, text="PDF报告:").pack(side=tk.LEFT)
        ttk.Entry(pdf_frame, textvariable=self.pdf_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(pdf_frame, text="浏览", command=lambda: self.select_save_path("pdf")).pack(side=tk.LEFT)

        # 折线图选择
        plot_frame = ttk.Frame(parent)
        plot_frame.pack(fill=tk.X, pady=5)
        ttk.Label(plot_frame, text="折线图:").pack(side=tk.LEFT)
        ttk.Entry(plot_frame, textvariable=self.plot_path, width=40).pack(side=tk.LEFT, padx=5)
        ttk.Button(plot_frame, text="浏览", command=lambda: self.select_save_path("plot")).pack(side=tk.LEFT)

    def select_excel_file(self):
        """选择Excel文件"""
        path = filedialog.askopenfilename(
            title="选择Excel文件",
            filetypes=[("Excel文件", "*.xlsx"), ("所有文件", "*.*")]
        )
        if path:
            self.excel_path.set(path)

    def select_save_path(self, file_type):
        """选择保存路径"""
        initial_dir = DEFAULT_CONFIG["default_dir"]
        filetypes = [("Word文档", "*.docx")]
        title = "保存Word报告"
        default_ext = ".docx"

        if file_type == "pdf":
            filetypes = [("PDF文档", "*.pdf")]
            title = "保存PDF报告"
            default_ext = ".pdf"
        elif file_type == "plot":
            filetypes = [("PNG图片", "*.png")]
            title = "保存折线图"
            default_ext = ".png"

        path = filedialog.asksaveasfilename(
            title=title,
            initialdir=initial_dir,
            filetypes=filetypes,
            defaultextension=default_ext
        )

        if path:
            if file_type == "word":
                self.word_path.set(path)
            elif file_type == "pdf":
                self.pdf_path.set(path)
            elif file_type == "plot":
                self.plot_path.set(path)

    def start_analysis(self):
        """开始分析"""
        # 验证Excel文件是否已选择
        if not self.excel_path.get():
            messagebox.showerror("错误", "请先选择Excel文件")
            return

        self.analyze_btn.config(state=tk.DISABLED)
        self.status_label.config(text="正在分析...", foreground="blue")
        self.progress.pack(pady=10)
        self.progress["value"] = 0
        self.root.update()

        # 在新线程中运行分析
        analysis_thread = threading.Thread(target=self.run_analysis, daemon=True)
        analysis_thread.start()

        # 启动进度条更新
        self.update_progress()

    def update_progress(self):
        """更新进度条"""
        if self.progress["value"] < 100:
            self.progress["value"] += 5
            self.root.after(200, self.update_progress)

    def run_analysis(self):
        """执行分析任务"""
        try:
            config = {
                "excel_path": self.excel_path.get(),
                "word_path": self.word_path.get(),
                "pdf_path": self.pdf_path.get(),
                "plot_path": self.plot_path.get(),** DEFAULT_CONFIG
            }

            # === 数据读取 ===
            self.update_status("正在读取Excel文件...")
            df = pd.read_excel(
                config['excel_path'],
                parse_dates=[config['time_col']],
                engine='openpyxl'
            )

            # === 数据验证 ===
            self.update_status("进行数据验证...")
            validate_data(df, config)

            # === 数据处理 ===
            self.update_status("执行质量评价...")
            evaluations = evaluate_tvoc_with_ml(df, config)

            # === 生成折线图 ===
            self.update_status("生成TVOC浓度折线图...")
            plt.figure(figsize=(10, 8))
            time_data = np.array(df[config['time_col']])
            value_data = np.array(df[config['value_col']])
            plt.plot(time_data, value_data, marker='o')
            plt.xlabel('检测时间')
            plt.ylabel('TVOC浓度(mg/m³)')
            plt.title('TVOC浓度变化趋势')
            plt.xticks(rotation=45)
            plt.grid(True)
            plt.savefig(config['plot_path'])
            plt.close()

            # === 生成报告 ===
            self.update_status("生成Word报告...")
            create_word_report(df, evaluations, config)

            # === 将Word文档转换为PDF ===
            self.update_status("将Word报告转换为PDF...")
            docx2pdf.convert(config['word_path'], config['pdf_path'])

            # 分析完成
            self.progress["value"] = 100
            self.update_status("分析完成!", success=True)

            # 计算统计结果
            grade_counts = {
                '优（安全）': evaluations.count('优（安全）'),
                '良（注意）': evaluations.count('良（注意）'),
                '差（需净化）': evaluations.count('差（需净化）')
            }
            total = len(df)

            # 显示结果
            result_message = (
                f"分析完成！\n\n"
                f"Word报告: {config['word_path']}\n"
                f"PDF报告: {config['pdf_path']}\n\n"
                f"TVOC浓度分布（GB/T 18883-2022标准）:\n"
                f"- 安全范围：{grade_counts['优（安全）']}次（≤0.6mg/m³）\n"
                f"- 注意范围：{grade_counts['良（注意）']}次（0.6-1.0mg/m³）\n"
                f"- 超标警报：{grade_counts['差（需净化）']}次（＞1.0mg/m³）"
            )
            messagebox.showinfo("分析完成", result_message)

        except FileNotFoundError:
            self.show_error(f"Excel文件不存在 {self.excel_path.get()}")
        except ValueError as ve:
            self.show_error(f"数据验证错误：{str(ve)}")
        except Exception as e:
            self.show_error(f"未预期的错误：{str(e)}")
        finally:
            # 恢复按钮状态
            self.analyze_btn.config(state=tk.NORMAL)

    def update_status(self, message, success=False):
        """更新状态标签"""
        self.status_label.config(text=message)
        if success:
            self.status_label.config(foreground="green")
        self.root.update()

    def show_error(self, message):
        """显示错误信息"""
        self.status_label.config(text="分析失败", foreground="red")
        self.progress["value"] = 0
        messagebox.showerror("错误", message)


# ==================== 数据验证函数 ====================
def validate_data(df, config):
    """验证TVOC数据有效性"""
    required_cols = [config['time_col'], config['value_col']]
    missing_cols = [col for col in required_cols if col not in df.columns]
    if missing_cols:
        raise ValueError(f"缺少必要列：{', '.join(missing_cols)}")

    try:
        # 验证数值范围（0-5 mg/m³）
        if (df[config['value_col']] < 0).any() or (df[config['value_col']] > 5).any():
            raise ValueError("TVOC检测值超出传感器有效范围(0-5mg/m³)")
    except Exception as e:
        print(f"数据验证时出错: {str(e)}")
        raise


# ==================== 使用机器学习进行质量评价函数 ====================
def evaluate_tvoc_with_ml(df, config):
    """使用决策树分类器进行TVOC质量等级评价"""
    # 准备数据
    X = df[[config['value_col']]]  # 特征
    y = df[config['value_col']].apply(lambda x: '优（安全）' if x <= config['thresholds']['优（安全）']
    else ('良（注意）' if x <= config['thresholds']['良（注意）']
          else '差（需净化）'))  # 标签

    # 划分训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

    # 训练模型
    model = DecisionTreeClassifier()
    model.fit(X_train, y_train)

    # 预测
    y_pred = model.predict(X)

    # 评估模型
    accuracy = accuracy_score(y, y_pred)
    print(f"模型准确率: {accuracy}")

    return y_pred.tolist()


# ==================== Word报告生成函数 ====================
def create_word_report(df, evaluations, config):
    """生成TVOC检测报告（含健康建议）"""
    # 确保保存目录存在
    os.makedirs(os.path.dirname(config['word_path']), exist_ok=True)

    doc = Document()

    # ---- 全局字体设置 ----
    doc.styles['Normal'].font.name = config['font_name']
    doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), config['font_name'])

    # ---- 报告标题 ----
    title = doc.add_paragraph()
    title.add_run("室内TVOC浓度检测报告").font.size = Pt(config['title_font_size'])
    title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER

    # ---- 基本信息段 ----
    doc.add_paragraph(f"报告周期：{df[config['time_col']].min()} 至 {df[config['time_col']].max()}")
    doc.add_paragraph(f"检测点位：{os.path.basename(config['excel_path'])}")

    # ---- 添加折线图 ----
    doc.add_heading('TVOC浓度变化趋势', level=1)
    doc.add_picture(config['plot_path'], width=Inches(6))

    doc.add_page_break()  # 分页
    # ---- 数据明细表 ----
    doc.add_heading('检测数据明细', level=1)
    table = doc.add_table(rows=1, cols=3, style=config['table_style'])
    header = table.rows[0].cells
    header[0].text = '记录时间'
    header[1].text = 'TVOC浓度(mg/m³)'
    header[2].text = '空气质量'

    for _, row in df.iterrows():
        cells = table.add_row().cells
        cells[0].text = str(row[config['time_col']])
        cells[1].text = f"{row[config['value_col']]:.3f}"  # 保留3位小数
        cells[2].text = evaluations[_]

    # ---- 统计分析段 ----
    grade_counts = {
        '优（安全）': evaluations.count('优（安全）'),
        '良（注意）': evaluations.count('良（注意）'),
        '差（需净化）': evaluations.count('差（需净化）')
    }
    total = len(df)

    doc.add_heading('空气质量评估', level=1)
    stats = [
        ("安全时段", f"{grade_counts['优（安全）']}次 ({grade_counts['优（安全）'] / total:.1%})"),
        ("注意时段", f"{grade_counts['良（注意）']}次 ({grade_counts['良（注意）'] / total:.1%})"),
        ("超标时段", f"{grade_counts['差（需净化）']}次 ({grade_counts['差（需净化）'] / total:.1%})"),
        ("峰值浓度", f"{df[config['value_col']].max():.3f} mg/m³"),
        ("日均浓度", f"{df[config['value_col']].mean():.3f} mg/m³")
    ]

    for label, value in stats:
        p = doc.add_paragraph(style='ListBullet')
        p.add_run(label).bold = True
        p.add_run(f": {value}")

    # ---- 健康建议段----
    doc.add_heading('健康建议', level=2)
    suggestions = [
        "超标时段建议：立即开窗通风，开启空气净化设备",
        "长期暴露风险：TVOC超标可能导致头痛、过敏反应",
        "数据解读：安全值≤0.6mg/m³，持续高于1.0mg/m³需采取治理措施"
    ]
    for item in suggestions:
        doc.add_paragraph(item, style='ListNumber')
        doc.save(config['word_path'])

# ==================== 主程序 ====================
if __name__ == "__main__":
    root = tk.Tk()
    app = TVOCAnalyzerUI(root)
    root.mainloop()
```

